pub use self::utils::export;
pub use self::vo::RowData;

pub mod tuleap_client {
    use std::{collections::HashMap, thread};
    use chrono::{NaiveDate, Duration};
    use office::{DataType, Excel};
    use serde_json::Map;
    use reqwest::{header::HeaderMap, Client};
    use serde_json::{Value, json};

    ///
    /// 处理excel日期类型
    /// 
    pub fn process_excel_date(excel_date_value: &f64) -> String {
        // Excel uses January 1st, 1900 as the origin date
        let origin_date = NaiveDate::from_ymd(1900, 1, 1);
    
        // Excel stores dates as the number of days since the origin date
        // let excel_date_value = 44967.0;
    
        // Convert the Excel date value to a number of days
        let days = excel_date_value.clone() as i64;
    
        // Add the number of days to the origin date to get the actual date
        let date = origin_date + Duration::days(days - 2);
    
        format!("{}", date.format("%Y-%m-%d"))
    }
    
    
    pub struct TuleapClient {
        pub api_url: String,
        pub access_key: String,
        pub client: Option<Client>,
        pub headers: HeaderMap,
    }

    impl TuleapClient {
        pub fn new(api_url: String, access_key: String) -> Self {
            // 初始化客户端
            let client = Self::builder_client();

            let mut headers = HeaderMap::new();
            headers.insert("Content-Type", "application/json".parse().unwrap());
            headers.insert("X-Auth-AccessKey", access_key.clone().parse().unwrap());

            Self {
                api_url,
                access_key,
                client: client,
                headers: headers,
            }
        }

        pub fn builder_client() -> Option<Client> {
            let client = reqwest::Client::builder()
                .danger_accept_invalid_certs(true)
                .build();

            match client {
                Ok(c) => Some(c),
                Err(e) => {
                    tracing::warn!("build client error :{:?}", e);
                    None
                }
            }
        }

        // tracker 详情
        pub async fn tracker_detail(
            &self,
            id: &u32,
        ) -> Result<HashMap<String, Value>, Box<dyn std::error::Error>> {
            let reqUrl = format!("{}api/trackers/{}", self.api_url, id);

            tracing::debug!("request url:{:?}", reqUrl);

            let resp = self
                .client
                .as_ref()
                .unwrap()
                .clone()
                .get(reqUrl)
                .headers(self.headers.clone())
                .send()
                .await?
                .json::<HashMap<String, Value>>()
                .await?;

            Ok(resp)
        }

        ///
        /// 根据标题搜索任务 like
        /// 
        pub async fn search_artifact_from_tracker_contains_title(&self,
            tracker_id: &u32,
            title: &String,
        ) -> Result<Value, Box<dyn std::error::Error>> {
            let query = json!({"title" : {"operator" : "contains", "value" : title}});
            let reqUrl = format!(
                "{}api/trackers/{}/artifacts?limit=1000&query={}&order=asc",
                self.api_url,
                tracker_id,
                format!("{}", query)
            );

            tracing::debug!(
                "search_artifact_from_tracker_contains_title request url:{:?}",
                reqUrl
            );

            
            let resp = self.client.as_ref().unwrap()
                .get(reqUrl)
                .headers(self.headers.clone())
                .send()
                .await?
                .json::<Value>()
                .await?;

            tracing::debug!("search_artifact_from_tracker_contains_title resp url:{:?}", resp);

            Ok(resp)
        }

        ///
        /// 根据标题搜索任务
        /// 
        pub async fn search_artifact_from_tracker_eq_title(&self,
            tracker_id: &u32,
            title: &String,
        ) -> Result<Value, Box<dyn std::error::Error>> {
            let query = json!({ "title": title });
            let reqUrl = format!(
                "{}api/trackers/{}/artifacts?limit=100&query={}&order=asc",
                self.api_url,
                tracker_id,
                format!("{}", query)
            );

            tracing::debug!(
                "search_artifact_from_tracker_eq_title request url:{:?}",
                reqUrl
            );

            
            let resp = self.client.as_ref().unwrap()
                .get(reqUrl)
                .headers(self.headers.clone())
                .send()
                .await?
                .json::<Value>()
                .await?;

            tracing::debug!("search_artifact_from_tracker_eq_title resp url:{:?}", resp);

            Ok(resp)
        }

        ///
        /// 获取tracker的fields定义
        /// 
        pub async fn tracker_fields(&self, tracker_id: &u32) -> HashMap<String, Map<String, Value>> {
            let mut fields = HashMap::<String, Map<String, Value>>::new();
            let detail = self.tracker_detail(tracker_id).await;
        
            if let Ok(data) = detail {
                let values = data.get("fields").unwrap();
        
                match values {
                    Value::Null => todo!(),
                    Value::Bool(_) => todo!(),
                    Value::Number(_) => todo!(),
                    Value::String(_) => todo!(),
                    Value::Array(vec) => {
                        for v in vec {
                            match v {
                                Value::Null => todo!(),
                                Value::Bool(_) => todo!(),
                                Value::Number(_) => todo!(),
                                Value::String(_) => todo!(),
                                Value::Array(_) => todo!(),
                                Value::Object(o) => {
                                    let k = o.get("label").unwrap();
                                    fields.insert(k.as_str().unwrap().to_string(), o.clone());
                                }
                            }
                        }
                    }
                    Value::Object(_) => todo!(),
                }
            }
        
            tracing::debug!("get_tracker_fields:{:?}", fields);
            fields
        }

        ///
        ///  导入excle数据到tuleap
        /// 
        pub async fn import(&self, tracker_id:&u32, path:&String, sheet_name: &String) {
            // 导入文件路径
            let path = path.clone();
            // 读取excel数据
            let mut workbook = Excel::open(path).unwrap();
        
            // Read whole worksheet data and provide some statistics
            if let Ok(range) = workbook.worksheet_range(sheet_name.clone().as_str()) {
                let mut title = HashMap::<usize, String>::new();
                let mut tracker_fields = HashMap::<String, Map<String, Value>>::new();
        
                for row in range.rows() {
                    if title.len() == 0 {
                        for (idx, d) in row.into_iter().enumerate() {
                            tracing::debug!("excel row:index:{:?}, value:{:?}", idx, d);
                            match d {
                                DataType::Int(_) => todo!(),
                                DataType::Float(_) => todo!(),
                                DataType::String(d) => {
                                    title.insert(idx, d.clone());
                                }
                                DataType::Bool(_) => todo!(),
                                DataType::Error(_) => todo!(),
                                DataType::Empty => {
                                    continue;
                                }
                            }
                        }
        
                        // 获取title 对应的field_id
                        tracker_fields = self.tracker_fields(tracker_id).await;
                    } else {
                        let mut title_and_data = HashMap::<String, String>::new();
                        let mut version = String::from("");
                        if let DataType::String(v) = row[1].clone() {
                            version = v;
                        }
        
                        for r in row.iter().enumerate() {
                            if let Some(key) = title.get(&r.0) {
                                match r.1 {
                                    DataType::Int(_) => todo!(),
                                    DataType::Float(v) => {
                                        // TODO 通过 tracker field 来判断是否为日期
                                        if key != "工期" && key != "实际工期" && key != "进度（%）"
                                        {
                                            let date = process_excel_date(v);
                                            title_and_data.insert(key.clone(), date);
                                        } else {
                                            // TODO 百分比字段处理
                                            if key == "进度（%）" {
                                                title_and_data
                                                    .insert(key.clone(), (v.clone() * 100.0).to_string());
                                            } else {
                                                title_and_data.insert(key.clone(), v.to_string());
                                            }
                                        }
                                    }
                                    DataType::String(v) => {
                                        // TODO 任务标题格式模版
                                        if key == "任务标题" {
                                            title_and_data.insert(
                                                key.clone(),
                                                format!("[{}]->{}", version, v.clone()),
                                            );
                                        } else {
                                            title_and_data.insert(key.clone(), v.clone());
                                        }
                                    }
                                    DataType::Bool(_) => todo!(),
                                    DataType::Error(_) => todo!(),
                                    DataType::Empty => {
                                        continue;
                                    }
                                }
                            }
                        }
                        tracing::debug!("title and data {:?}", title_and_data);
                        // 判断任务是否已经创建
                        match self.search_artifact_from_tracker_eq_title(
                            &tracker_id,
                            title_and_data.get("任务标题").unwrap(),
                        )
                        .await
                        {
                            Ok(data) => {
                                if let Value::Array(v) = data {
                                    let json_val =
                                        self.create_artifact_entity(tracker_id, &title, &tracker_fields, &title_and_data);
                                    if v.len() == 0 {
                                        if let Ok(res) = self.create_artifact(&json_val).await {
                                            tracing::info!("create success:{:?}", res);
                                        } else {
                                            tracing::warn!("create failed:{:?}", &json_val);
                                        }
                                    } else {
                                        let artifac_id = v[0]
                                            .as_object()
                                            .unwrap()
                                            .get("id")
                                            .unwrap()
                                            .as_u64()
                                            .unwrap();
                                        tracing::info!(
                                            "update artifact id:{:?}, update values: {}",
                                            artifac_id,
                                            &json_val
                                        );
        
                                        match self.update_artifact_by_id(
                                            &artifac_id,
                                            &json_val,
                                            false,
                                        ).await{
                                            Ok(res) => { tracing::info!("update success:{:?}", res);},
                                            Err(e) => {tracing::warn!("update failed:{:?}, error:{:?}", &json_val, e)},
                                        }
                                    }
                                }
                            }
                            Err(err) => {}
                        }
        
                        thread::sleep(std::time::Duration::from_secs(1));
                    }
                }
            }
        }

        ///
        /// 发送请求创建artifact
        /// 
        async fn create_artifact(&self,
            values: &Value,
        ) -> Result<HashMap<String, Value>, Box<dyn std::error::Error>> {
            let reqUrl = format!("{}api/artifacts", self.api_url);
        
            tracing::debug!("create_artifact req url {:#?}", reqUrl);
        
            let resp = self.client.as_ref().unwrap()
                .post(reqUrl)
                .headers(self.headers.clone())
                .json(&values)
                .send()
                .await?
                .json::<HashMap<String, Value>>()
                .await?;
            Ok(resp)
        }

        ///
        /// 构造 artifact 请求体
        /// 
        fn create_artifact_entity(&self,
            tracker_id: &u32,
            title: &HashMap<usize, String>,
            tracker_fields: &HashMap<String, Map<String, Value>>,
            data: &HashMap<String, String>,
        ) -> Value {
            let mut r_data = json!({
              "tracker": {
                "id": tracker_id,
              },
              "values": [
                
              ],
            });
        
            let mut values = Vec::<Value>::new();
        
            for t in data {
                if t.0 == "计划状态" {
                    continue;
                }
        
                if let Some(field_object) = tracker_fields.get(t.0) {
                    let field_id = field_object.get("field_id").unwrap().as_u64().unwrap();
        
                    let mut v_d = json!({ "field_id": field_id });
                    let r_val = t.1;
        
                    // 处理多选对象
                    if let Some(bindings) = field_object.get("bindings") {
                        // bind_value_ids
                        if let Some(bindings_type) = bindings.get("type") {
                            match bindings_type {
                                Value::Null => {
                                    tracing::debug!("value:{:?}", t);
                                    v_d.as_object_mut()
                                        .unwrap()
                                        .append(json!({ "value": r_val }).as_object_mut().unwrap());
                                }
                                Value::Bool(_) => todo!(),
                                Value::Number(_) => todo!(),
                                Value::String(t) => {
                                    let values = field_object.get("values").unwrap().as_array().unwrap();
                                    if "static" != t {
                                        // 引用类型
                                        if let Some(list) = bindings.get("list") {
                                            let mut ids: Vec<Value> = Vec::new();
                                            ids = list
                                                .as_array()
                                                .unwrap()
                                                .into_iter()
                                                .map(|x| { x.get("id").unwrap() }.clone())
                                                .collect();
        
                                            tracing::debug!("list ids:{:?}", ids);
                                            v_d.as_object_mut().unwrap().append(
                                                json!({ "bind_value_ids": ids }).as_object_mut().unwrap(),
                                            );
        
                                            let mut selected: Vec<Value> = self.get_ref_select(r_val, values);
                                            v_d.as_object_mut().unwrap().append(
                                                json!({ "bind_value_ids": selected })
                                                    .as_object_mut()
                                                    .unwrap(),
                                            );
                                        }
                                    } else {
                                        // 静态值
                                        let mut selected: Vec<Value> = self.get_static_select(r_val, values);
                                        v_d.as_object_mut().unwrap().append(
                                            json!({ "bind_value_ids": selected })
                                                .as_object_mut()
                                                .unwrap(),
                                        );
                                    }
                                }
                                Value::Array(_) => todo!(),
                                Value::Object(_) => todo!(),
                            }
        
                            tracing::debug!("bindings_type type:{:?}", bindings_type);
                        }
                    }
                    values.push(v_d);
                }
            }
            tracing::debug!("values:{:?}", values);
        
            r_data
                .as_object_mut()
                .unwrap()
                .append(json!({ "values": values }).as_object_mut().unwrap());
        
            r_data
        }

        ///
        /// 更新 artifact 根据 is_delete 配置，做覆盖更新或删除更新
        /// 如果是删除更新，需要在tuleap配置tracker的批量删除数量，以及token的删除权限开通
        /// 
        async fn update_artifact_by_id(&self,
            id: &u64,
            values: &Value,
            is_delete: bool,
        ) -> Result<HashMap<String, Value>, Box<dyn std::error::Error>> {
            if is_delete {
                // 先删除后更新
                self.delete_artifact_by_id(id).await;

                // 创建新的artifact
                let res = self.create_artifact(&values).await;

                res
            } else {
                let reqUrl = format!("{}api/artifacts/{}", self.api_url, id);

                tracing::debug!("update_artifact req url {:#?}", reqUrl);

                let resp = self.client.as_ref().unwrap()
                    .put(reqUrl)
                    .headers(self.headers.clone())
                    .json(&values)
                    .send()
                    .await?
                    .json::<HashMap<String, Value>>()
                    .await?;
                Ok(resp)
            }
        }

        ///
        /// 根据 id 删除 artifact
        /// 
        pub async fn delete_artifact_by_id(
            &self,
            id: &u64,
        ) -> Result<Value, Box<dyn std::error::Error>> {

            let reqUrl = format!("{}api/artifacts/{}", self.api_url, id);

            tracing::debug!("delete artifact req url {:#?}", reqUrl);

            let resp = self.client.as_ref().unwrap()
                .delete(reqUrl)
                .headers(self.headers.clone())
                .send()
                .await?
                .json::<Value>()
                .await?;

            tracing::debug!("delete artifact resp {:#?}", &resp);
            Ok(resp)
        }
        
        ///
        /// 匹配 ref 类型值，获取id
        /// 
        fn get_ref_select(&self, r_val: &str, values: &[Value]) -> Vec<Value> {
            let texts: Vec<_> = r_val.split(",").collect();
        
            tracing::trace!("selected text:{:?}", texts);
        
            let mut ids: Vec<Value> = Vec::new();
            ids = values
                .iter()
                .filter(|x| {
                    let names: Vec<_> = x
                        .get("user_reference")
                        .unwrap()
                        .as_object()
                        .unwrap()
                        .get("real_name")
                        .unwrap()
                        .as_str()
                        .unwrap()
                        .split(" ")
                        .collect();
                    texts.contains(names.get(0).unwrap())
                })
                .map(|x| { x.get("id").unwrap() }.clone())
                .collect();
            ids
        }
        
        fn get_static_select(&self,r_val: &String, values: &Vec<Value>) -> Vec<Value> {
            let texts: Vec<_> = r_val.split(",").collect();
        
            tracing::trace!("selected text:{:?}", texts);
        
            let mut ids: Vec<Value> = Vec::new();
            ids = values
                .iter()
                .filter(|x| texts.contains(&x.get("label").unwrap().as_str().unwrap()))
                .map(|x| { x.get("id").unwrap() }.clone())
                .collect();
            ids
        }
    }
}

pub mod vo {
    use std::collections::HashMap;

    #[derive(Debug)]
    pub struct RowData {
        pub level: String,
        pub plan_title: String,
        pub task_name: String,
        pub client_type: String,
        pub plan_user: String,
        pub modules: String,
        pub assignees: String,
        pub task_type: String,
        pub joint_users: String,
        pub plan_date: String,
        pub process: String,
        pub test_status: String,
        pub plan_status: String,
        pub status: String,
        pub time_limit: String,
        pub real_time_limit: String,
        pub plan_begin_date: String,
        pub plan_finsh_date: String,
        pub real_begin_date: String,
        pub real_finsh_date: String,
        pub remark: String,
        pub detail: String,
        pub id: String,
    }
    fn format_date(s: Option<&String>) -> String {
        match s {
            Some(str) => {
                let ary = str.as_str();
                if ary.len() > 10 {
                    ary[0..10].to_string()
                } else {
                    ary.to_string()
                }
            }
            None => String::from(""),
        }
    }
    impl RowData {
        pub fn new_plan(plan_title: String, task_name: String) -> RowData {
            let mut res = RowData::new();

            res.level = "1".to_string();
            res.plan_title = plan_title;
            res.task_name = task_name;

            res
        }

        pub fn new_story(plan_title: String, task_name: String) -> RowData {
            let mut res = RowData::new_plan(plan_title, task_name);
            res.level = "2".to_string();
            res
        }

        pub fn new_task(
            plan_title: String,
            task_name: String,
            client_type: String,
            plan_user: String,
            modules: String,
            assignees: String,
            task_type: String,
            joint_users: String,
            plan_date: String,
            process: String,
            test_status: String,
            plan_status: String,
            status: String,
            time_limit: String,
            real_time_limit: String,
            plan_begin_date: String,
            plan_finsh_date: String,
            real_begin_date: String,
            real_finsh_date: String,
            remark: String,
            detail: String,
            id: String,
        ) -> RowData {
            let mut res = RowData::new_plan(plan_title, task_name);
            res.level = "3".to_string();

            res.client_type = client_type;
            res.plan_user = plan_user;
            res.modules = modules;
            res.assignees = assignees;
            res.task_type = task_type;
            res.plan_date = plan_date;
            res.joint_users = joint_users;
            res.process = process;
            res.test_status = test_status;
            res.plan_status = plan_status;
            res.status = status;
            res.time_limit = time_limit;
            res.real_time_limit = real_time_limit;
            res.plan_begin_date = plan_begin_date;
            res.plan_finsh_date = plan_finsh_date;
            res.real_begin_date = real_begin_date;
            res.real_finsh_date = real_finsh_date;
            res.remark = remark;
            res.detail = detail;
            res.id = id;

            res
        }

        pub fn new_task_map(
            plan_title: String,
            task_name: String,
            values: HashMap<String, String>,
        ) -> RowData {
            RowData::new_task(
                plan_title,
                task_name,
                format!("{}", values["终端"]),
                values["规划人"].to_string(),
                values["涉及模块"].to_string(),
                values["负责人"].to_string(),
                values["任务类型"].to_string(),
                values["参与人"].to_string(),
                format_date(values.get("发起时间")),
                String::from(""), //values["进度（%）"].to_string(),
                values["自测状态"].to_string(),
                values["计划内状态"].to_string(),
                values["当前状态"].to_string(),
                values["工期"].to_string(),
                values["实际工期"].to_string(),
                format_date(values.get("预计开始时间")),
                format_date(values.get("预计完成时间")),
                format_date(values.get("实际开始时间")),
                format_date(values.get("实际结束时间")),
                values["备注"].to_string(),
                values["任务描述"].to_string(),
                values["Artifact ID"].to_string(),
            )
        }

        fn new() -> RowData {
            RowData {
                level: "".to_string(),
                plan_title: "".to_string(),
                task_name: "".to_string(),
                client_type: "".to_string(),
                plan_user: "".to_string(),
                modules: "".to_string(),
                assignees: "".to_string(),
                task_type: "".to_string(),
                joint_users: "".to_string(),
                plan_date: "".to_string(),
                process: "".to_string(),
                test_status: "".to_string(),
                plan_status: "".to_string(),
                status: "".to_string(),
                time_limit: "".to_string(),
                real_time_limit: "".to_string(),
                plan_begin_date: "".to_string(),
                plan_finsh_date: "".to_string(),
                real_begin_date: "".to_string(),
                real_finsh_date: "".to_string(),
                remark: "".to_string(),
                detail: "".to_string(),
                id: "".to_string(),
            }
        }
    }
}

pub mod utils {
    
    use super::RowData;
    use chrono::{prelude::*, Duration};
    use simple_excel_writer::*;
    use uuid::Uuid;

  
    pub fn export(rowDatas: Vec<RowData>) -> String {
        //本地存储路径
        let save_path = "./";

        // 当前时间
        let date = Local::now().format("%Y-%m-%d").to_string();
        std::fs::create_dir(format!("{}{}/", save_path, date.clone()));
        let uid = Uuid::new_v4().to_string()[0..8].to_string();
        let filename = format!("{}{}/{}.xlsx", save_path, date, uid);

        let mut wb = Workbook::create(&filename);
        let mut sheet = wb.create_sheet("第一页");
        // 设置行宽
        sheet.add_column(Column { width: 30.0 });
        sheet.add_column(Column { width: 30.0 });
        sheet.add_column(Column { width: 30.0 });
        sheet.add_column(Column { width: 30.0 });

        wb.write_sheet(&mut sheet, |sheet_writer| {
            let sw = sheet_writer;
            sw.append_row(row![
                "级别",
                "版本 ",
                "终端",
                "涉及模块",
                "任务类型",
                "任务发起时间",
                "规划人（方案）",
                "负责人",
                "工期（天）",
                "预计开始时间",
                "预计完成时间",
                "计划状态",
                "当前状态",
                "测试状态",
                "进度（百分比）",
                "实际工期（天）",
                "实际开始时间",
                "实际完成时间",
                "参与人",
                "任务标题",
                "任务描述",
                "备注",
                "link"
            ])?;

            for rd in rowDatas {
                sw.append_row(row![
                    rd.level,
                    rd.plan_title,
                    rd.client_type.replace("\"", ""),
                    rd.modules.replace("\"", ""),
                    rd.task_type.replace("\"", ""),
                    rd.plan_date,
                    rd.plan_user,
                    rd.assignees,
                    rd.time_limit,
                    rd.plan_begin_date,
                    rd.plan_finsh_date,
                    rd.plan_status.replace("\"", ""),
                    rd.status.replace("\"", ""),
                    rd.test_status.replace("\"", ""),
                    rd.process,
                    rd.real_time_limit,
                    rd.real_begin_date,
                    rd.real_finsh_date,
                    rd.joint_users,
                    rd.task_name,
                    rd.detail,
                    rd.remark,
                    format!("https://192.168.1.20/plugins/tracker/?aid={}", rd.id)
                ])?;
            }

            Ok(())
        })
        .expect("写入excel错误!");

        wb.close().expect("关闭excel错误!");

        filename
    }
}
